﻿using Manon.Core.Commons;
using Manon.Core.Extensions;
using Manon.Core.Jwt;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Manon.Core.IOC
{
    public static class ResolverExtensions
    {
        public static IServiceCollection RegisterServices(this IServiceCollection services, params string[] projectNames)
        {
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddScoped<IApplicationUser, ApplicationUser>();
            services.AddSingleton<IJwtAppService, JwtAppService>();
            Assembly[] assemblies = GetAllAssembliesByProjectName(services, projectNames);

            return RegisterAllServices(services, assemblies);

        }

        public static IServiceCollection RegisterServices(this IServiceCollection services)
        {
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddScoped<IApplicationUser, ApplicationUser>();
            services.AddSingleton<IJwtAppService, JwtAppService>();
            Assembly[] assemblies = GetAllAssemblies(services);
            return RegisterAllServices(services, assemblies);
        }

        private static Assembly[] GetAllAssemblies(IServiceCollection services, params string[] projectNames)
        {
            List<Assembly> AllAssemblies = new List<Assembly>();

            string path = AppDomain.CurrentDomain.RelativeSearchPath ?? System.AppDomain.CurrentDomain.BaseDirectory;

            Assembly[] appAssemblies = Directory.GetFiles(path, $"*.Application.dll").Select(m => Assembly.LoadFrom(m)).ToArray();
            AllAssemblies.AddRange(appAssemblies);

            Assembly[] domainAssemblies = Directory.GetFiles(path, $"*.Domain.dll").Select(m => Assembly.LoadFrom(m)).ToArray();
            AllAssemblies.AddRange(domainAssemblies);

            Assembly[] domainSharedAssemblies = Directory.GetFiles(path, $"*.Domain.Shared.dll").Select(m => Assembly.LoadFrom(m)).ToArray();
            AllAssemblies.AddRange(domainSharedAssemblies);

            return AllAssemblies.ToArray();
        }

        private static Assembly[] GetAllAssembliesByProjectName(IServiceCollection services, params string[] projectNames)
        {
            List<Assembly> AllAssemblies = new List<Assembly>();
            if (projectNames == null || projectNames.Length == 0) return AllAssemblies.ToArray();


            string path = AppDomain.CurrentDomain.RelativeSearchPath ?? System.AppDomain.CurrentDomain.BaseDirectory;
            foreach (var projectName in projectNames)
            {
                Assembly[] assemblies = Directory.GetFiles(path, $"{projectName}.dll").Select(m => Assembly.LoadFrom(m)).ToArray();
                AllAssemblies.AddRange(assemblies);
            }
            return AllAssemblies.ToArray();
        }



        private static IServiceCollection RegisterAllServices(IServiceCollection services, Assembly[] assemblies)
        {
            services.AddSingleton<IRandomCode, RandomCode>();

            Type transientInstanceType = typeof(ITransientDependency);
            Type singleInstanceType = typeof(ISingletonDependency);
            Type scopeInstanceType = typeof(IScopeDependency);

            foreach (var assembly in assemblies)
            {
                var transientTypes = assembly.GetTypes()
                                             .Where(x => transientInstanceType.IsAssignableFrom(x) && x.IsAbstract == false);

                foreach (var serviceType in transientTypes)
                {
                    var interFaces = serviceType.GetInterfaces();
                    foreach (var serviceInterface in interFaces)
                    {
                        if (string.Equals(serviceInterface.Name, "ITransientDependency", StringComparison.CurrentCultureIgnoreCase)
                            || string.Equals(serviceInterface.Name, "IDependency", StringComparison.CurrentCultureIgnoreCase)) continue;
                        services.AddTransient(serviceInterface, serviceType);
                    }
                }

                var singleTypes = assembly.GetTypes()
                                          .Where(x => singleInstanceType.IsAssignableFrom(x) && x.IsAbstract == false);

                foreach (var serviceType in singleTypes)
                {
                    var interFaces = serviceType.GetInterfaces();
                    foreach (var serviceInterface in interFaces)
                    {
                        if (string.Equals(serviceInterface.Name, "ISingletonDependency", StringComparison.CurrentCultureIgnoreCase)
                            || string.Equals(serviceInterface.Name, "IDependency", StringComparison.CurrentCultureIgnoreCase)) continue;
                        services.AddSingleton(serviceInterface, serviceType);
                    }
                }

                var scopeTypes = assembly.GetTypes()
                                         .Where(x => scopeInstanceType.IsAssignableFrom(x) && x.IsAbstract == false);

                foreach (var serviceType in scopeTypes)
                {
                    var interFaces = serviceType.GetInterfaces();
                    foreach (var serviceInterface in interFaces)
                    {
                        if (string.Equals(serviceInterface.Name, "IScopeDependency", StringComparison.CurrentCultureIgnoreCase)
                             || string.Equals(serviceInterface.Name, "IDependency", StringComparison.CurrentCultureIgnoreCase)) continue;
                        services.AddScoped(serviceInterface, serviceType);
                    }
                }
            }
            return services;

        }

    }
}
